unit bson;

interface

uses
  SysUtils;

type
  EBsonUnknownType = class(Exception);
  EBsonInvalidHexDigit = class(Exception);
  EBsonError = class(Exception);

const
  BSON_OK = 0;
  BSON_ERROR = -1;

  initialBufferSize = 128;

type
  TBsonValidityFlags = (
    bvfBsonSizeOverflow = 0,    // Trying to create a BSON object larger than High(Integer).
    bvfBsonNotUtf8 = 1,         // A key or a string is not valid UTF-8.
    bvfBsonFieldHasDot = 2,     // Warning: key contains '.' character.
    bvfBsonFieldInitDollar = 3, // Warning: key starts with '$' character.
    bvfBsonAlreadyFinished = 4  // Trying to modify a finished BSON object.
  );
  TBsonValidity = set of TBsonValidityFlags;

  TBsonBinarySubtype = (
    bbsBsonBinBinary = 0,
    bbsBsonBinFunc = 1,
    bbsBsonBinBinaryOld = 2,
    bbsBsonBinUuid = 3,
    bbsBsonBinMd5 = 5,
    bbsBsonBinUser = 128
  );

  TBsonType = (
    btBsonEoo = 0,
    btBsonDouble = 1,
    btBsonString = 2,
    btBsonObject = 3,
    btBsonArray = 4,
    btBsonBindata = 5,
    btBsonUndefined = 6,
    btBsonOid = 7,
    btBsonBool = 8,
    btBsonDate = 9,
    btBsonNull = 10,
    btBsonRegex = 11,
    btBsonDbref = 12, // Deprecated.
    btBsonCode = 13,
    btBsonSymbol = 14,
    btBsonCodewscope = 15,
    btBsonInt = 16,
    btBsonTimestamp = 17,
    btBsonLong = 18
  );
  PBsonType = ^TBsonType;

  TBsonIterator = record
    cur: PByte;
    first: Boolean;
  end;
  PBsonIterator = ^TBsonIterator;

  TBson = record
    data: PByte;
    cur: PByte;
    dataSize: Integer;
    finished: Boolean;
    stack: array[0..31] of Integer;
    stackPos: Integer;
    err: TBsonValidity; // Set representing errors or warnings on this buffer
    errstr: string; // A string representation of the most recent error or warning
  end;
  PBson = ^TBson;

  TBsonOid = packed record
  case ByBytes: Boolean of
    True: (
      bytes: array[0..11] of Byte;
    );
    False: (
      ints: array[0..2] of Integer;
    );
  end;
  PBsonOid = ^TBsonOid;

  TBsonDate = Int64;

  TBsonTimestamp = record
    i: Integer; // increment
    t: Integer; // time in seconds
  end;

  Tbson_printf = reference to function(const Format: string; const Args: array of const): string;
  Tbson_int_func = reference to function: Integer;

var
  bson_printf: Tbson_printf = nil;

// ----------------------------
// READING
// ------------------------------

//
// Size of a BSON object.
//
// @param b the BSON object.
//
// @return the size.
//
function bson_size(const b: TBson): Integer;

//
// Print a string representation of a BSON object.
//
// @param b the BSON object to print.
//
procedure bson_print(const b: TBson);

//
// Return a pointer to the raw buffer stored by this bson object.
//
// @param b a BSON object
//
function bson_data(var b: TBson): PByte;

//
// Print a string representation of a BSON object.
//
// @param bson the raw data to print.
// @param depth the depth to recurse the object.x
//
procedure bson_print_raw(const data: PByte; depth: Integer);

//
// Advance a bson_iterator to the named field.
//
// @param it the bson_iterator to use.
// @param obj the BSON object to use.
// @param name the name of the field to find.
//
// @return the type of the found object or BSON_EOO if it is not found.
//
function bson_find(var it: TBsonIterator; const obj: TBson; name: string ): TBsonType;

//
// Initialize a bson_iterator.
//
// @param i the bson_iterator to initialize.
// @param bson the BSON object to associate with the iterator.
//
procedure bson_iterator_init(var i: TBsonIterator; const b: TBson);

//
// Initialize a bson iterator from a const char* buffer. Note
// that this is mostly used internally.
//
// @param i the bson_iterator to initialize.
// @param buffer the buffer to point to.
//
procedure bson_iterator_from_buffer(var i: TBsonIterator; const buffer: PByte);

// more returns true for eoo. best to loop with bson_iterator_next(&it)
//
// Check to see if the bson_iterator has more data.
//
// @param i the iterator.
//
// @return  returns true if there is more data.
//
function bson_iterator_more(const i: TBsonIterator): Boolean;

//
// Point the iterator at the next BSON object.
//
// @param i the bson_iterator.
//
// @return the type of the next BSON object.
//
function bson_iterator_next(var i: TBsonIterator): TBsonType;

//
// Get the type of the BSON object currently pointed to by the iterator.
//
// @param i the bson_iterator
//
// @return  the type of the current BSON object.
//
function bson_iterator_type(const i: TBsonIterator): TBsonType;

//
// Get the key of the BSON object currently pointed to by the iterator.
//
// @param i the bson_iterator
//
// @return the key of the current BSON object.
//
function bson_iterator_key(const i: TBsonIterator): string;

//
// Get the value of the BSON object currently pointed to by the iterator.
//
// @param i the bson_iterator
//
// @return  the value of the current BSON object.
//
function bson_iterator_value(const i: TBsonIterator): PByte;

// these convert to the right type (return 0 if non-numeric)
//
// Get the double value of the BSON object currently pointed to by the
// iterator.
//
// @param i the bson_iterator
//
// @return  the value of the current BSON object.
//
function bson_iterator_double(const i: TBsonIterator): Double;

//
// Get the int value of the BSON object currently pointed to by the iterator.
//
// @param i the bson_iterator
//
// @return  the value of the current BSON object.
//
function bson_iterator_int(const i: TBsonIterator): Integer;

//
// Get the long value of the BSON object currently pointed to by the iterator.
//
// @param i the bson_iterator
//
// @return the value of the current BSON object.
//
function bson_iterator_long(const i: TBsonIterator): Int64;

// return the bson timestamp as a whole or in parts
//
// Get the timestamp value of the BSON object currently pointed to by
// the iterator.
//
// @param i the bson_iterator
//
// @return the value of the current BSON object.
//
function bson_iterator_timestamp(const i: TBsonIterator): TBsonTimestamp;

//
// Get the boolean value of the BSON object currently pointed to by
// the iterator.
//
// @param i the bson_iterator
//
// @return the value of the current BSON object.
//
// false: boolean false, 0 in any type, or null
// true: anything else (even empty strings and objects)
function bson_iterator_bool(const i: TBsonIterator): Boolean;

//
// Get the double value of the BSON object currently pointed to by the
// iterator. Assumes the correct type is used.
//
// @param i the bson_iterator
//
// @return the value of the current BSON object.
//
// these assume you are using the right type
function bson_iterator_double_raw(const i: TBsonIterator): Double;

//
// Get the int value of the BSON object currently pointed to by the
// iterator. Assumes the correct type is used.
//
// @param i the bson_iterator
//
// @return the value of the current BSON object.
//
function bson_iterator_int_raw(const i: TBsonIterator): Integer;

//
// Get the long value of the BSON object currently pointed to by the
// iterator. Assumes the correct type is used.
//
// @param i the bson_iterator
//
// @return the value of the current BSON object.
//
function bson_iterator_long_raw(const i: TBsonIterator): Int64;

//
// Get the bson_bool_t value of the BSON object currently pointed to by the
// iterator. Assumes the correct type is used.
//
// @param i the bson_iterator
//
// @return the value of the current BSON object.
//
function bson_iterator_bool_raw(const i: TBsonIterator): Boolean;

//
// Get the bson_oid_t value of the BSON object currently pointed to by the
// iterator.
//
// @param i the bson_iterator
//
// @return the value of the current BSON object.
//
function bson_iterator_oid(const i: TBsonIterator): TBsonOid;

//
// Get the string value of the BSON object currently pointed to by the
// iterator.
//
// @param i the bson_iterator
//
// @return  the value of the current BSON object.
//
// these can also be used with bson_code and bson_symbol
function bson_iterator_string(const i: TBsonIterator): string;

//
// Get the string length of the BSON object currently pointed to by the
// iterator.
//
// @param i the bson_iterator
//
// @return the length of the current BSON object.
//
function bson_iterator_string_len(const i: TBsonIterator): Integer;

//
// Get the code value of the BSON object currently pointed to by the
// iterator. Works with bson_code, bson_codewscope, and BSON_STRING
// returns NULL for everything else.
//
// @param i the bson_iterator
//
// @return the code value of the current BSON object.
//
// works with bson_code, bson_codewscope, and BSON_STRING */
// returns NULL for everything else */
function bson_iterator_code(const i: TBsonIterator): string;

//
// Calls bson_empty on scope if not a bson_codewscope
//
// @param i the bson_iterator.
// @param scope the bson scope.
//
// calls bson_empty on scope if not a bson_codewscope */
procedure bson_iterator_code_scope(const i: TBsonIterator; var scope: TBson);

//
// Get the date value of the BSON object currently pointed to by the
// iterator.
//
// @param i the bson_iterator
//
// @return the date value of the current BSON object.
//
// both of these only work with bson_date
function bson_iterator_date(const i: TBsonIterator): TBsonDate;

//
// Get the time value of the BSON object currently pointed to by the
// iterator.
//
// @param i the bson_iterator
//
// @return the time value of the current BSON object.
//
function bson_iterator_time_t(const i: TBsonIterator): Int64;

//
// Get the length of the BSON binary object currently pointed to by the
// iterator.
//
// @param i the bson_iterator
//
// @return the length of the current BSON binary object.
//
function bson_iterator_bin_len(const i: TBsonIterator): Integer;

//
// Get the type of the BSON binary object currently pointed to by the
// iterator.
//
// @param i the bson_iterator
//
// @return the type of the current BSON binary object.
//
function bson_iterator_bin_type(const i: TBsonIterator): TBsonBinarySubtype;

//
// Get the value of the BSON binary object currently pointed to by the
// iterator.
//
// @param i the bson_iterator
//
// @return the value of the current BSON binary object.
//
function bson_iterator_bin_data(const i: TBsonIterator): PByte;

//
// Get the value of the BSON regex object currently pointed to by the
// iterator.
//
// @param i the bson_iterator
//
// @return the value of the current BSON regex object.
//
function bson_iterator_regex(const i: TBsonIterator): string;

//
// Get the options of the BSON regex object currently pointed to by the
// iterator.
//
// @param i the bson_iterator.
//
// @return the options of the current BSON regex object.
//
function bson_iterator_regex_opts(const i: TBsonIterator): string;

// these work with BSON_OBJECT and BSON_ARRAY
//
// Get the BSON subobject currently pointed to by the
// iterator.
//
// @param i the bson_iterator.
// @param sub the BSON subobject destination.
//
procedure bson_iterator_subobject(const i: TBsonIterator; var sub: TBson);

//
// Get a bson_iterator that on the BSON subobject.
//
// @param i the bson_iterator.
// @param sub the iterator to point at the BSON subobject.
//
procedure bson_iterator_subiterator(const i: TBsonIterator; var sub: TBsonIterator);

// str must be at least 24 hex chars + null byte
//
// Create a bson_oid_t from a string.
//
// @param oid the bson_oid_t destination.
// @param str a null terminated string comprised of at least 24 hex chars.
//
procedure bson_oid_from_string(var oid: TBsonOid; str: string);

//
// Create a string representation of the bson_oid_t.
//
// @param oid the bson_oid_t source.
// @param str the string representation destination.
//
procedure bson_oid_to_string(const oid: TBsonOid; var str: string);

//
// Create a bson_oid object.
//
// @param oid the destination for the newly created bson_oid_t.
//
procedure bson_oid_gen(var oid: TBsonOid);

//
// Set a function to be used to generate the second four bytes
// of an object id.
//
// @param func a pointer to a function that returns an int.
//
procedure bson_set_oid_fuzz(func: Tbson_int_func);

//
// Set a function to be used to generate the incrementing part
// of an object id (last four bytes). If you need thread-safety
// in generating object ids, you should set this function.
//
// @param func a pointer to a function that returns an int.
//
procedure bson_set_oid_inc(func: Tbson_int_func);

//
// Get the time a bson_oid_t was created.
//
// @param oid the bson_oid_t.
//
function bson_oid_generated_time(const oid: TBsonOid ): Int64; // Gives the time the OID was created

// ----------------------------
// BUILDING
// ------------------------------

//
//  Initialize a new bson object. If not created
//  with bson_new, you must initialize each new bson
//  object using this function.
//
//  @note When finished, you must pass the bson object to
//      bson_destroy( ).
//
procedure bson_init(var b: TBson);

//
// Initialize a BSON object, and point its data
// pointer to the provided char*.
//
// @param b the BSON object to initialize.
// @param data the raw BSON data.
//
// @return BSON_OK or BSON_ERROR.
//
function bson_init_data(var b: TBson; data: PByte): Integer;

//
// pointer to the provided char*. We assume
// that the data represents a finished BSON object.
//
// @param b the BSON object to initialize.
// @param data the raw BSON data.
//
// @return BSON_OK or BSON_ERROR.
//
function bson_init_finished_data(var b: TBson; data: PByte ): Integer;

//
// Initialize a BSON object, and set its
// buffer to the given size.
//
// @param b the BSON object to initialize.
// @param size the initial size of the buffer.
//
// @return BSON_OK or BSON_ERROR.
//
procedure bson_init_size(var b: TBson; size: Integer);

//
// Grow a bson object.
//
// @param b the bson to grow.
// @param bytesNeeded the additional number of bytes needed.
//
// @return BSON_OK or BSON_ERROR with the bson error object set.
//   Exits if allocation fails.
//
function bson_ensure_space(var b: TBson; bytesNeeded: Integer): Integer;

//
// Finalize a bson object.
//
// @param b the bson object to finalize.
//
// @return the standard error code. To deallocate memory,
//   call bson_destroy on the bson object.
//
function bson_finish(var b: TBson): Integer;

//
// Destroy a bson object.
//
// @param b the bson object to destroy.
//
//
procedure bson_destroy(var b: TBson);

//
// Returns a pointer to a static empty BSON object.
//
// @param obj the BSON object to initialize.
//
// @return the empty initialized BSON object.
//
// returns pointer to static empty bson object
function bson_empty(var obj: TBson): PBson;

//
// Make a complete copy of the a BSON object.
// The source bson object must be in a finished
// state; otherwise, the copy will fail.
//
// @param out the copy destination BSON object.
// @param in the copy source BSON object.
//
function bson_copy(var o: TBson; const i: TBson): Integer; // puts data in new buffer. NOOP if out==NULL

//
// Append a previously created bson_oid_t to a bson object.
//
// @param b the bson to append to.
// @param name the key for the bson_oid_t.
// @param oid the bson_oid_t to append.
//
// @return BSON_OK or BSON_ERROR.
//
function bson_append_oid(var b: TBson; name: string; const oid: TBsonOid): Integer;

//
// Append a bson_oid_t to a bson.
//
// @param b the bson to append to.
// @param name the key for the bson_oid_t.
//
// @return BSON_OK or BSON_ERROR.
//
function bson_append_new_oid(var b: TBson; name: string): Integer;

//
// Append an int to a bson.
//
// @param b the bson to append to.
// @param name the key for the int.
// @param i the int to append.
//
// @return BSON_OK or BSON_ERROR.
//
function bson_append_int(var b: TBson; name: string; i: Integer): Integer;

//
// Append an long to a bson.
//
// @param b the bson to append to.
// @param name the key for the long.
// @param i the long to append.
//
// @return BSON_OK or BSON_ERROR.
//
function bson_append_long(var b: TBson; name: string; i: Int64): Integer;

//
// Append an double to a bson.
//
// @param b the bson to append to.
// @param name the key for the double.
// @param d the double to append.
//
// @return BSON_OK or BSON_ERROR.
//
function bson_append_double(var b: TBson; name: string; d: Double): Integer;

//
// Append a string to a bson.
//
// @param b the bson to append to.
// @param name the key for the string.
// @param str the string to append.
//
// @return BSON_OK or BSON_ERROR.
//
function bson_append_string(var b: TBson; name, str: string): Integer;

//
// Append len bytes of a string to a bson.
//
// @param b the bson to append to.
// @param name the key for the string.
// @param str the string to append.
// @param len the number of bytes from str to append.
//
// @return BSON_OK or BSON_ERROR.
//
function bson_append_string_n(var b: TBson; name, str: string; len: Integer): Integer;

//
// Append a symbol to a bson.
//
// @param b the bson to append to.
// @param name the key for the symbol.
// @param str the symbol to append.
//
// @return BSON_OK or BSON_ERROR.
//
function bson_append_symbol(var b: TBson; name, str: string): Integer;

//
// Append len bytes of a symbol to a bson.
//
// @param b the bson to append to.
// @param name the key for the symbol.
// @param str the symbol to append.
// @param len the number of bytes from str to append.
//
// @return BSON_OK or BSON_ERROR.
//
function bson_append_symbol_n(var b: TBson; name, str: string; len: Integer): Integer;

//
// Append code to a bson.
//
// @param b the bson to append to.
// @param name the key for the code.
// @param str the code to append.
// @param len the number of bytes from str to append.
//
// @return BSON_OK or BSON_ERROR.
//
function bson_append_code(var b: TBson; name, str: string): Integer;

//
// Append len bytes of code to a bson.
//
// @param b the bson to append to.
// @param name the key for the code.
// @param str the code to append.
// @param len the number of bytes from str to append.
//
// @return BSON_OK or BSON_ERROR.
//
function bson_append_code_n(var b: TBson; name, str: string; len: Integer): Integer;

//
// Append code to a bson with scope.
//
// @param b the bson to append to.
// @param name the key for the code.
// @param str the string to append.
// @param scope a BSON object containing the scope.
//
// @return BSON_OK or BSON_ERROR.
//
function bson_append_code_w_scope(var b: TBson; name, code: string; const scope: TBson): Integer;

//
// Append len bytes of code to a bson with scope.
//
// @param b the bson to append to.
// @param name the key for the code.
// @param str the string to append.
// @param len the number of bytes from str to append.
// @param scope a BSON object containing the scope.
//
// @return BSON_OK or BSON_ERROR.
//
function bson_append_code_w_scope_n(var b: TBson; name, code: string; len: Integer; const scope: TBson): Integer;

//
// Append binary data to a bson.
//
// @param b the bson to append to.
// @param name the key for the data.
// @param type the binary data type.
// @param str the binary data.
// @param len the length of the data.
//
// @return BSON_OK or BSON_ERROR.
//
function bson_append_binary(var b: TBson; name: string; t: TBsonBinarySubtype; str: PByte; len: Integer): Integer;

//
// Append a bson_bool_t to a bson.
//
// @param b the bson to append to.
// @param name the key for the boolean value.
// @param v the bson_bool_t to append.
//
// @return BSON_OK or BSON_ERROR.
//
function bson_append_bool(var b: TBson; name: string; v: Boolean): Integer;

//
// Append a null value to a bson.
//
// @param b the bson to append to.
// @param name the key for the null value.
//
// @return BSON_OK or BSON_ERROR.
//
function bson_append_null(var b: TBson; name: string): Integer;

//
// Append an undefined value to a bson.
//
// @param b the bson to append to.
// @param name the key for the undefined value.
//
// @return BSON_OK or BSON_ERROR.
//
function bson_append_undefined(var b: TBson; name: string): Integer;

//
// Append a regex value to a bson.
//
// @param b the bson to append to.
// @param name the key for the regex value.
// @param pattern the regex pattern to append.
// @param the regex options.
//
// @return BSON_OK or BSON_ERROR.
//
function bson_append_regex(var b: TBson; name, pattern, opts: string): Integer;

//
// Append bson data to a bson.
//
// @param b the bson to append to.
// @param name the key for the bson data.
// @param bson the bson object to append.
//
// @return BSON_OK or BSON_ERROR.
//
function bson_append_bson(var b: TBson; name: string; const bson: TBson): Integer;

//
// Append a BSON element to a bson from the current point of an iterator.
//
// @param b the bson to append to.
// @param name_or_null the key for the BSON element, or NULL.
// @param elem the bson_iterator.
//
// @return BSON_OK or BSON_ERROR.
//
function bson_append_element(var b: TBson; name_or_null: PString; const elem: TBsonIterator): Integer;

//
// Append a bson_timestamp_t value to a bson.
//
// @param b the bson to append to.
// @param name the key for the timestampe value.
// @param ts the bson_timestamp_t value to append.
//
// @return BSON_OK or BSON_ERROR.
//
function bson_append_timestamp(var b: TBson; name: string; const ts: TBsonTimestamp): Integer;

// these both append a bson_date
//
// Append a bson_date_t value to a bson.
//
// @param b the bson to append to.
// @param name the key for the date value.
// @param millis the bson_date_t to append.
//
// @return BSON_OK or BSON_ERROR.
//
function bson_append_date(var b: TBson; name: string; millis: TBsonDate): Integer;

//
// Append a time_t value to a bson.
//
// @param b the bson to append to.
// @param name the key for the date value.
// @param secs the time_t to append.
//
// @return BSON_OK or BSON_ERROR.
//
function bson_append_time_t(var b: TBson; name: string; secs: Int64): Integer;

//
// Start appending a new object to a bson.
//
// @param b the bson to append to.
// @param name the name of the new object.
//
// @return BSON_OK or BSON_ERROR.
//
function bson_append_start_object(var b: TBson; name: string): Integer;

//
// Start appending a new array to a bson.
//
// @param b the bson to append to.
// @param name the name of the new array.
//
// @return BSON_OK or BSON_ERROR.
//
function bson_append_start_array(var b: TBson; name: string): Integer;

//
// Finish appending a new object or array to a bson.
//
// @param b the bson to append to.
//
// @return BSON_OK or BSON_ERROR.
//
function bson_append_finish_object(var b: TBson): Integer;

//
// Finish appending a new object or array to a bson. This
// is simply an alias for bson_append_finish_object.
//
// @param b the bson to append to.
//
// @return BSON_OK or BSON_ERROR.
//
function bson_append_finish_array(var b: TBson): Integer;

implementation

uses
  Math,
  StrUtils,
  DateUtils,
  platform,
  encoding;

const
  zero: Integer = 0;

var
  oid_fuzz_func: Tbson_int_func = nil;
  oid_inc_func: Tbson_int_func = nil;

procedure _bson_reset(var b: TBson);
begin
  b.finished := False;
  b.stackPos := 0;
  b.err := [];
  b.errstr := '';
end;

procedure _bson_init_size(var b: TBson; size: Integer);
begin
  if size = 0 then
    b.data := nil
  else
    GetMem(b.data, size);

  b.dataSize := size;
  b.cur := b.data + 4;
  _bson_reset(b);
end;

procedure _bson_append_byte(var b: TBson; c: Byte);
begin
  b.cur[0] := c;
  Inc(b.cur);
end;

procedure _bson_append(var b: TBson; data: PByte; len: Integer);
begin
  Move(data^, b.cur^, len);
  Inc(b.cur, len);
end;

procedure _bson_append32(var b: TBson; data: PByte);
begin
  bson_little_endian32(b.cur^, data^);
  Inc(b.cur, 4);
end;

procedure _bson_append64(var b: TBson; data: PByte);
begin
  bson_little_endian64(b.cur^, data^);
  Inc(b.cur, 8);
end;

function _bson_append_estart(var b: TBson; t: TBsonType; name: string; dataSize: Integer): Integer;
var
  len: Integer;
  nameUtf8: UTF8String;
begin
  nameUtf8 := UTF8String(name);
  len := Length(nameUtf8) + 1;

  if b.finished then begin
    Include(b.err, bvfBsonAlreadyFinished);
    Exit(BSON_ERROR);
  end;

  if bson_ensure_space(b, 1 + len + dataSize) = BSON_ERROR then
    Exit(BSON_ERROR);

  if bson_check_field_name(b, PAnsiChar(nameUtf8), len - 1) = BSON_ERROR then begin
    raise EBsonError.Create('BSON error');
    Exit(BSON_ERROR);
  end;

  _bson_append_byte(b, Ord(t));
  _bson_append(b, PByte(PAnsiChar(nameUtf8)), len);
  Result := BSON_OK;
end;

function _bson_append_string_base(var b: TBson; name, value: string; t: TBsonType): Integer;
var
  valueUtf8: UTF8String;
  sl: Integer;
begin
  valueUtf8 := UTF8String(value);
  sl := Length(valueUtf8) + 1;
  if bson_check_string(b, PAnsiChar(valueUtf8), sl - 1) = BSON_ERROR then
    Exit(BSON_ERROR);
  if _bson_append_estart(b, t, name, 4 + sl) = BSON_ERROR then
    Exit(BSON_ERROR);
  _bson_append32(b, @sl);
  _bson_append(b, PByte(PAnsiChar(valueUtf8)), sl);
  Result := BSON_OK;
end;

function bson_size(const b: TBson): Integer;
begin
  if not Assigned(b.data) then
    Exit(0);
  bson_little_endian32(Result, b.data^);
end;

procedure bson_print(const b: TBson);
begin
  bson_print_raw(b.data, 0);
end;

function bson_data(var b: TBson): PByte;
begin
  Result := b.data;
end;

procedure bson_print_raw(const data: PByte; depth: Integer);
var
  i: TBsonIterator;
  t: TBsonType;
  key: string;
  oidhex: string;
  temp: Integer;
  scope: TBson;
  ts: TBsonTimestamp;
begin
  bson_iterator_from_buffer(i, data);

  while bson_iterator_next(i) > btBsonEoo do begin
    t := bson_iterator_type(i);
    if t = btBsonEoo then
      Break;
    key := bson_iterator_key(i);

    for temp := 0 to depth do
      bson_printf(#$0009, []);
    bson_printf('%s : %d '#$0009' ', [key, Ord(t)]);
    case t of
      btBsonDouble:
        bson_printf('%f', [bson_iterator_double(i)]);
      btBsonString:
        bson_printf('%s', [bson_iterator_string(i)]);
      btBsonSymbol:
        bson_printf('SYMBOL: %s', [bson_iterator_string(i)]);
      btBsonOid: begin
        bson_oid_to_string(bson_iterator_oid(i), oidhex);
        bson_printf('%s', [oidhex]);
      end;
      btBsonBool:
        bson_printf('%s', [IfThen(bson_iterator_bool(i), 'true', 'false')]);
      btBsonDate:
        bson_printf('%d', [bson_iterator_date(i)]);
      btBsonBindata:
        bson_printf('BSON_BINDATA', []);
      btBsonUndefined:
        bson_printf('BSON_UNDEFINED', []);
      btBsonNull:
        bson_printf('BSON_NULL', []);
      btBsonRegex:
        bson_printf('BSON_REGEX: %s', [bson_iterator_regex(i)]);
      btBsonCode:
        bson_printf('BSON_CODE: %s', [bson_iterator_code(i)]);
      btBsonCodewscope: begin
        bson_printf('BSON_CODE_W_SCOPE: %s', [bson_iterator_code(i)]);
//        Don't want to do this since it'll end up with a leak.  FillChar() and the following function should
//        be sufficient.
//        bson_init(scope);
        FillChar(scope, SizeOf(scope), 0);
        bson_iterator_code_scope(i, scope);
        bson_printf(#$000D#$000A#$0009' SCOPE: ', []);
        bson_print(scope);
      end;
      btBsonInt:
        bson_printf('%d', [bson_iterator_int(i)]);
      btBsonLong:
        bson_printf('%d', [bson_iterator_long(i)]);
      btBsonTimestamp: begin
        ts := bson_iterator_timestamp(i);
        bson_printf('i: %d, t: %d', [ts.i, ts.t]);
      end;
      btBsonObject,
      btBsonArray: begin
        bson_printf(#$000D#$000A, []);
        bson_print_raw(bson_iterator_value(i), depth + 1);
      end;
    else
      raise EBsonUnknownType.CreateFmt('can''t print type : %d', [Ord(t)]);
    end;
  end;
end;

function bson_find(var it: TBsonIterator; const obj: TBson; name: string ): TBsonType;
begin
  bson_iterator_init(it, obj);
  while bson_iterator_next(it) > btBsonEoo do begin
    if name = bson_iterator_key(it) then
      Break;
  end;
  Result := bson_iterator_type(it);
end;

procedure bson_iterator_init(var i: TBsonIterator; const b: TBson);
begin
  i.cur := b.data + 4;
  i.first := True;
end;

procedure bson_iterator_from_buffer(var i: TBsonIterator; const buffer: PByte);
begin
  i.cur := buffer + 4;
  i.first := True;
end;

function bson_iterator_more(const i: TBsonIterator): Boolean;
begin
  Result := i.cur^ > 0;
end;

function bson_iterator_next(var i: TBsonIterator): TBsonType;
var
  ds: Cardinal;
  s, p: PByte;
begin
  if i.first then begin
    i.first := False;
    Exit(TBsonType(i.cur^));
  end;

  case bson_iterator_type(i) of
    btBsonEoo:        Exit(btBsonEoo); // don't advance
    btBsonUndefined,
    btBsonNull:       ds := 0;
    btBsonBool:       ds := 1;
    btBsonInt:        ds := 4;
    btBsonLong,
    btBsonDouble,
    btBsonTimestamp,
    btBsonDate:       ds := 8;
    btBsonOid:        ds := 12;
    btBsonString,
    btBsonSymbol,
    btBsonCode:       ds := 4 + bson_iterator_int_raw(i);
    btBsonBindata:    ds := 5 + bson_iterator_int_raw(i);
    btBsonObject,
    btBsonArray,
    btBsonCodewscope: ds := bson_iterator_int_raw(i);
    btBsonDbref:      ds := 4 + 12 + bson_iterator_int_raw(i);
    btBsonRegex: begin
      s := bson_iterator_value(i);
      p := s;
      Inc(p, StrLen(PAnsiChar(p)) + 1);
      Inc(p, StrLen(PAnsiChar(p)) + 1);
      ds := p - s;
    end;
  else
    raise EBsonUnknownType.CreateFmt('Unknown type: %d', [PLongword(i.cur)^]);
  end;

  Inc(i.cur, 1 + StrLen(PAnsiChar(i.cur + 1)) + 1 + ds);

  Result := PBsonType(i.cur)^;
end;

function bson_iterator_type(const i: TBsonIterator): TBsonType;
begin
  Result := PBsonType(i.cur)^;
end;

function bson_iterator_key(const i: TBsonIterator): string;
begin
  Result := string(UTF8String(PAnsiChar(i.cur + 1)));
end;

function bson_iterator_value(const i: TBsonIterator): PByte;
begin
  Result := i.cur + 1;
  Inc(Result, StrLen(PAnsiChar(Result)) + 1);
end;

function bson_iterator_double(const i: TBsonIterator): Double;
begin
  case bson_iterator_type(i) of
    btBsonInt:    Result := bson_iterator_int_raw(i);
    btBsonLong:   Result := bson_iterator_long_raw(i);
    btBsonDouble: Result := bson_iterator_double_raw(i);
  else
    Result := 0;
  end;
end;

function bson_iterator_int(const i: TBsonIterator): Integer;
begin
  case bson_iterator_type(i) of
    btBsonInt:    Result := bson_iterator_int_raw(i);
    btBsonLong:   Result := bson_iterator_long_raw(i);
    btBsonDouble: Result := Trunc(bson_iterator_double_raw(i));
  else
    Result := 0;
  end;
end;

function bson_iterator_long(const i: TBsonIterator): Int64;
begin
  case bson_iterator_type(i) of
    btBsonInt:    Result := bson_iterator_int_raw(i);
    btBsonLong:   Result := bson_iterator_long_raw(i);
    btBsonDouble: Result := Trunc(bson_iterator_double_raw(i));
  else
    Result := 0;
  end;
end;

function bson_iterator_timestamp(const i: TBsonIterator): TBsonTimestamp;
var
  v: PByte;
begin
  v := bson_iterator_value(i);
  bson_little_endian32(Result.i, v^);
  Inc(v, 4);
  bson_little_endian32(Result.t, v^);
end;

function bson_iterator_bool(const i: TBsonIterator): Boolean;
begin
  case bson_iterator_type(i) of
    btBsonBool:   Result := bson_iterator_bool_raw(i);
    btBsonInt:    Result := bson_iterator_int_raw(i) <> 0;
    btBsonLong:   Result := bson_iterator_long_raw(i) <> 0;
    btBsonDouble: Result := bson_iterator_double_raw(i) <> 0;
    btBsonEoo,
    btBsonNull:   Result := False;
  else
    Result := True;
  end;
end;

function bson_iterator_double_raw(const i: TBsonIterator): Double;
var
  v: PByte;
begin
  v := bson_iterator_value(i);
  bson_little_endian64(Result, v^);
end;

function bson_iterator_int_raw(const i: TBsonIterator): Integer;
var
  v: PByte;
begin
  v := bson_iterator_value(i);
  bson_little_endian32(Result, v^);
end;

function bson_iterator_long_raw(const i: TBsonIterator): Int64;
var
  v: PByte;
begin
  v := bson_iterator_value(i);
  bson_little_endian64(Result, v^);
end;

function bson_iterator_bool_raw(const i: TBsonIterator): Boolean;
begin
  Result := bson_iterator_value(i)^ <> 0;
end;

function bson_iterator_oid(const i: TBsonIterator): TBsonOid;
begin
  Result := PBsonOid(bson_iterator_value(i))^;
end;

function bson_iterator_string(const i: TBsonIterator): string;
begin
  Result := string(UTF8String(PAnsiChar(bson_iterator_value(i) + 4)));
end;

function bson_iterator_string_len(const i: TBsonIterator): Integer;
begin
  Result := bson_iterator_int_raw(i);
end;

function bson_iterator_code(const i: TBsonIterator): string;
begin
  case bson_iterator_type(i) of
    btBsonString,
    btBsonCode:       Result := string(UTF8String(PAnsiChar(bson_iterator_value(i) + 4)));
    btBsonCodewscope: Result := string(UTF8String(PAnsiChar(bson_iterator_value(i) + 8)));
  else
    Result := '';
  end;
end;

procedure bson_iterator_code_scope(const i: TBsonIterator; var scope: TBson);
var
  v: PByte;
  code_len: Longword;
begin
  if bson_iterator_type(i) = btBsonCodewscope then begin
    v := bson_iterator_value(i) + 4;
    bson_little_endian32(code_len, v^);
    bson_init_data(scope, v + 8 + code_len);
    _bson_reset(scope);
    scope.finished := True;
  end
  else begin
    bson_empty(scope);
  end;
end;

function bson_iterator_date(const i: TBsonIterator): TBsonDate;
begin
  Result := bson_iterator_long_raw(i);
end;

function bson_iterator_time_t(const i: TBsonIterator): Int64;
begin
  Result := bson_iterator_date(i) div 1000;
end;

function bson_iterator_bin_len(const i: TBsonIterator): Integer;
begin
  Result := IfThen(bson_iterator_bin_type(i) = bbsBsonBinBinaryOld,
    bson_iterator_int_raw(i) - 4,
    bson_iterator_int_raw(i));
end;

function bson_iterator_bin_type(const i: TBsonIterator): TBsonBinarySubtype;
begin
  Result := TBsonBinarySubtype(bson_iterator_value(i)[4]);
end;

function bson_iterator_bin_data(const i: TBsonIterator): PByte;
begin
  Result := PByte(IfThen(bson_iterator_bin_type(i) = bbsBsonBinBinaryOld,
    NativeInt(bson_iterator_value(i) + 9),
    NativeInt(bson_iterator_value(i) + 5)));
end;

function bson_iterator_regex(const i: TBsonIterator): string;
begin
  Result := string(UTF8String(PAnsiChar(bson_iterator_value(i))));
end;

function bson_iterator_regex_opts(const i: TBsonIterator): string;
var
  p: PByte;
begin
  p := bson_iterator_value(i);
  Result := string(UTF8String(PAnsiChar(p + StrLen(PAnsiChar(p)) + 1)));
end;

procedure bson_iterator_subobject(const i: TBsonIterator; var sub: TBson);
begin
  bson_init_data(sub, bson_iterator_value(i));
  _bson_reset(sub);
  sub.finished := True;
end;

procedure bson_iterator_subiterator(const i: TBsonIterator; var sub: TBsonIterator);
begin
  bson_iterator_from_buffer(sub, bson_iterator_value(i));
end;

function hexbyte(hex: Char): Byte;
begin
  if (hex >= '0') and (hex <= '9') then
    Result := Ord(hex) - Ord('0')
  else if (hex >= 'a') and (hex <= 'f') then
    Result := Ord(hex) - Ord('a') + 10
  else if (hex >= 'A') and (hex <= 'F') then
    Result := Ord(hex) - Ord('A') + 10
  else
    raise EBsonInvalidHexDigit.CreateFmt('invalid hex digit: %s', [hex]);
end;

procedure bson_oid_from_string(var oid: TBsonOid; str: string);
var
  i: Integer;
begin
  for i := 0 to 11 do
    oid.bytes[i] := (hexbyte(str[2 * i + 1]) shl 4) or (hexbyte(str[2 * i + 2]));
end;

procedure bson_oid_to_string(const oid: TBsonOid; var str: string);
const
  hex: array[0..15] of Char = ('0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e',
    'f');
var
  i: Integer;
begin
  SetLength(str, 24);
  for i := 0 to 11 do begin
    str[2 * i + 1] := hex[(oid.bytes[i] and $F0) shr 4];
    str[2 * i + 2] := hex[oid.bytes[i] and $0F];
  end;
end;

procedure bson_oid_gen(var oid: TBsonOid);
var
  incr: Integer;
  fuzz: Integer;
  t: Integer;
  i: Integer;
begin
  incr := 0;
  fuzz := 0;
  if not Assigned(oid_inc_func) then begin
    // This works because it creates a closure over incr that basically lives as long as this anonymous
    // function lives...creating an incrementer.
    oid_inc_func := function: Integer
      begin
        Result := incr;
        Inc(incr);
      end;
  end;

  if not Assigned(oid_fuzz_func) then begin
    // Similar to above, except this just randomly generates some fuzz value that remains identical for the
    // lifetime of this anonymous function.
    oid_fuzz_func := function: Integer
      begin
        if fuzz = 0 then begin
          Randomize;
          fuzz := Random(Integer($FFFFFFFF)); // Full 32-bit range
        end;
        Result := fuzz;
      end;
  end;

  t := Integer(DateTimeToUnix(Now));
  i := oid_inc_func;
  bson_big_endian32(oid.ints[0], t);
  oid.ints[1] := oid_fuzz_func;
  bson_big_endian32(oid.ints[2], i);
end;

procedure bson_set_oid_fuzz(func: Tbson_int_func);
begin
  oid_fuzz_func := func;
end;

procedure bson_set_oid_inc(func: Tbson_int_func);
begin
  oid_inc_func := func;
end;

function bson_oid_generated_time(const oid: TBsonOid ): Int64; // Gives the time the OID was created
begin
  bson_big_endian32(Result, oid.ints[0]);
end;

procedure bson_init(var b: TBson);
begin
  _bson_init_size(b, initialBufferSize);
end;

function bson_init_data(var b: TBson; data: PByte): Integer;
begin
  b.data := data;
  Result := BSON_OK;
end;

function bson_init_finished_data(var b: TBson; data: PByte ): Integer;
begin
  bson_init_data(b, data);
  b.stackPos := 0;
  b.finished := True;
  Result := BSON_OK;
end;

procedure bson_init_size(var b: TBson; size: Integer);
begin
  _bson_init_size(b, size);
end;

function bson_ensure_space(var b: TBson; bytesNeeded: Integer): Integer;
var
  pos: Integer;
  orig: PByte;
  new_size: Integer;
begin
  pos := b.cur - b.data;
  orig := b.data;

  if (pos + bytesNeeded) <= b.dataSize then
    Exit(BSON_OK);

  new_size := Trunc(1.5 * (b.dataSize + bytesNeeded));

  if new_size < b.dataSize then begin
    if (b.dataSize + bytesNeeded) < High(Integer) then
      new_size := High(Integer)
    else begin
      b.err := [bvfBsonSizeOverflow];
      Exit(BSON_ERROR);
    end;
  end;

  ReallocMem(b.data, new_size);
  b.dataSize := new_size;
  Inc(b.cur, b.data - orig);
  Result := BSON_OK;
end;

function bson_finish(var b: TBson): Integer;
var
  i: Integer;
begin
  if bvfBsonNotUtf8 in b.err then
    Exit(BSON_ERROR);

  if not b.finished then begin
    if bson_ensure_space(b, 1) = BSON_ERROR then Exit(BSON_ERROR);
    _bson_append_byte(b, 0);
    i := b.cur - b.data;
    bson_little_endian32(b.data^, i);
    b.finished := True;
  end;

  Result := BSON_OK;
end;

procedure bson_destroy(var b: TBson);
begin
  FreeMem(b.data);
  b.err := [];
  b.data := nil;
  b.cur := nil;
  b.finished := True;
end;

function bson_empty(var obj: TBson): PBson;
const
  data: array[0..4] of Byte = (5, 0, 0, 0, 0);
begin
  bson_init_data(obj, @data);
  obj.finished := True;
  obj.err := [];
  obj.stackPos := 0;
  Result := @obj;
end;

function bson_copy(var o: TBson; const i: TBson): Integer; // puts data in new buffer. NOOP if out==NULL
begin
  if not i.finished then Exit(BSON_ERROR);
  bson_init_size(o, bson_size(i));
  Move(i.data^, o.data^, bson_size(i));
  o.finished := True;

  Result := BSON_OK;
end;

function bson_append_oid(var b: TBson; name: string; const oid: TBsonOid): Integer;
begin
  if _bson_append_estart(b, btBsonOid, name, 12) = BSON_ERROR then
    Exit(BSON_ERROR);
  _bson_append(b, @oid, 12);
  Result := BSON_OK;
end;

function bson_append_new_oid(var b: TBson; name: string): Integer;
var
  oid: TBsonOid;
begin
  bson_oid_gen(oid);
  Result := bson_append_oid(b, name, oid);
end;

function bson_append_int(var b: TBson; name: string; i: Integer): Integer;
begin
  if _bson_append_estart(b, btBsonInt, name, 4) = BSON_ERROR then
    Exit(BSON_ERROR);
  _bson_append32(b, @i);
  Result := BSON_OK;
end;

function bson_append_long(var b: TBson; name: string; i: Int64): Integer;
begin
  if _bson_append_estart(b, btBsonLong, name, 8) = BSON_ERROR then
    Exit(BSON_ERROR);
  _bson_append64(b, @i);
  Result := BSON_OK;
end;

function bson_append_double(var b: TBson; name: string; d: Double): Integer;
begin
  if _bson_append_estart(b, btBsonDouble, name, 8) = BSON_ERROR then
    Exit(BSON_ERROR);
  _bson_append64(b, @d);
  Result := BSON_OK;
end;

function bson_append_string(var b: TBson; name, str: string): Integer;
begin
  Result := _bson_append_string_base(b, name, str, btBsonString);
end;

function bson_append_string_n(var b: TBson; name, str: string; len: Integer): Integer;
begin
  Result := _bson_append_string_base(b, name, LeftStr(str, len), btBsonString);
end;

function bson_append_symbol(var b: TBson; name, str: string): Integer;
begin
  Result := _bson_append_string_base(b, name, str, btBsonSymbol);
end;

function bson_append_symbol_n(var b: TBson; name, str: string; len: Integer): Integer;
begin
  Result := _bson_append_string_base(b, name, LeftStr(str, len), btBsonSymbol);
end;

function bson_append_code(var b: TBson; name, str: string): Integer;
begin
  Result := _bson_append_string_base(b, name, str, btBsonCode);
end;

function bson_append_code_n(var b: TBson; name, str: string; len: Integer): Integer;
begin
  Result := _bson_append_string_base(b, name, LeftStr(str, len), btBsonCode);
end;

function bson_append_code_w_scope(var b: TBson; name, code: string; const scope: TBson): Integer;
begin
  Result := bson_append_code_w_scope_n(b, name, code, Length(code), scope);
end;

function bson_append_code_w_scope_n(var b: TBson; name, code: string; len: Integer; const scope: TBson): Integer;
var
  codeUtf8: Utf8String;
  sl,
  size: Integer;
begin
  codeUtf8 := UTF8String(Copy(Code, len));
  sl := Length(codeUtf8) + 1;
  size := 4 + 4 + sl + bson_size(scope);
  if _bson_append_estart(b, btBsonCodewscope, name, size) = BSON_ERROR then
    Exit(BSON_ERROR);
  _bson_append32(b, @size);
  _bson_append32(b, @sl);
  _bson_append(b, PByte(PAnsiChar(codeUtf8)), sl);
  _bson_append(b, scope.data, bson_size(scope));
  Result := BSON_OK;
end;

function bson_append_binary(var b: TBson; name: string; t: TBsonBinarySubtype; str: PByte; len: Integer): Integer;
var
  subtwolen: Integer;
begin
  if t = bbsBsonBinBinaryOld then begin
    subtwolen := len + 4;
    if _bson_append_estart(b, btBsonBindata, name, 4 + 1 + 4 + len) = BSON_ERROR then
      Exit(BSON_ERROR);
    _bson_append32(b, @subtwolen);
    _bson_append_byte(b, Ord(t));
    _bson_append32(b, @len);
    _bson_append(b, str, len);
  end
  else begin
    if _bson_append_estart(b, btBsonBindata, name, 4 + 1 + len) = BSON_ERROR then
      Exit(BSON_ERROR);
    _bson_append32(b, @len);
    _bson_append_byte(b, Ord(t));
    _bson_append(b, str, len);
  end;
  Result := BSON_OK;
end;

function bson_append_bool(var b: TBson; name: string; v: Boolean): Integer;
begin
  if _bson_append_estart(b, btBsonBool, name, 1) = BSON_ERROR then
    Exit(BSON_ERROR);
  _bson_append_byte(b, Ord(v));
  Result := BSON_OK;
end;

function bson_append_null(var b: TBson; name: string): Integer;
begin
  if _bson_append_estart(b, btBsonNull, name, 0) = BSON_ERROR then
    Exit(BSON_ERROR);
  Result := BSON_OK;
end;

function bson_append_undefined(var b: TBson; name: string): Integer;
begin
  if _bson_append_estart(b, btBsonUndefined, name, 0) = BSON_ERROR then
    Exit(BSON_ERROR);
  Result := BSON_OK;
end;

function bson_append_regex(var b: TBson; name, pattern, opts: string): Integer;
var
  patternUtf8,
  optsUtf8: UTF8String;
  plen,
  olen: Integer;
begin
  patternUtf8 := UTF8String(pattern);
  optsUtf8 := UTF8String(opts);
  plen := Length(patternUtf8) + 1;
  olen := Length(optsUtf8) + 1;
  if _bson_append_estart(b, btBsonRegex, name, plen + olen) = BSON_ERROR then
    Exit(BSON_ERROR);
  if bson_check_string(b, PAnsiChar(patternUtf8), plen - 1) = BSON_ERROR then
    Exit(BSON_ERROR);
  _bson_append(b, PByte(PAnsiChar(patternUtf8)), plen);
  _bson_append(b, PByte(PAnsiChar(optsUtf8)), olen);
  Result := BSON_OK;
end;

function bson_append_bson(var b: TBson; name: string; const bson: TBson): Integer;
begin
  if _bson_append_estart(b, btBsonObject, name, bson_size(bson)) = BSON_ERROR then
    Exit(BSON_ERROR);
  _bson_append(b, bson.data, bson_size(bson));
  Result := BSON_OK;
end;

function bson_append_element(var b: TBson; name_or_null: PString; const elem: TBsonIterator): Integer;
var
  next: TBsonIterator;
  size,
  data_size: Integer;
begin
  next := elem;

  bson_iterator_next(next);
  size := next.cur - elem.cur;

  if not Assigned(name_or_null) then begin
    if bson_ensure_space(b, size) = BSON_ERROR then
      Exit(BSON_ERROR);
    _bson_append(b, elem.cur, size);
  end
  else begin
    data_size := size - 2 - Length(UTF8String(bson_iterator_key(elem)));
    _bson_append_estart(b, TBsonType(elem.cur^), name_or_null^, data_size);
    _bson_append(b, bson_iterator_value(elem), data_size);
  end;

  Result := BSON_OK;
end;

function bson_append_timestamp(var b: TBson; name: string; const ts: TBsonTimestamp): Integer;
begin
  if _bson_append_estart(b, btBsonTimestamp, name, 8) = BSON_ERROR then
    Exit(BSON_ERROR);

  _bson_append32(b, @(ts.i));
  _bson_append32(b, @(ts.t));

  Result := BSON_OK;
end;

function bson_append_date(var b: TBson; name: string; millis: TBsonDate): Integer;
begin
  if _bson_append_estart(b, btBsonDate, name, 8) = BSON_ERROR then
    Exit(BSON_ERROR);
  _bson_append64(b, @millis);
  Result := BSON_OK;
end;

function bson_append_time_t(var b: TBson; name: string; secs: Int64): Integer;
begin
  Result := bson_append_date(b, name, secs * 1000);
end;

function bson_append_start_object(var b: TBson; name: string): Integer;
begin
  if _bson_append_estart(b, btBsonObject, name, 5) = BSON_ERROR then
    Exit(BSON_ERROR);
  b.stack[b.stackPos] := b.cur - b.data;
  Inc(b.stackPos);
  _bson_append32(b, @zero);
  Result := BSON_OK;
end;

function bson_append_start_array(var b: TBson; name: string): Integer;
begin
  if _bson_append_estart(b, btBsonArray, name, 5) = BSON_ERROR then
    Exit(BSON_ERROR);
  b.stack[b.stackPos] := b.cur - b.data;
  Inc(b.stackPos);
  _bson_append32(b, @zero);
  Result := BSON_OK;
end;

function bson_append_finish_object(var b: TBson): Integer;
var
  start: PByte;
  i: NativeInt;
begin
  if bson_ensure_space(b, 1) = BSON_ERROR then
    Exit(BSON_ERROR);
  _bson_append_byte(b, 0);

  Dec(b.stackPos);
  start := b.data + b.stack[b.stackPos];
  i := b.cur - start;
  bson_little_endian32(start^, i);

  Result := BSON_OK;
end;

function bson_append_finish_array(var b: TBson): Integer;
begin
  Result := bson_append_finish_object(b);
end;

end.
